use agent::Agent;
use evol::World;
use game::{Game, Human};
use rand::thread_rng;
use skill::Normal;
use skill::Skill;
use std::{
    fs::{File, OpenOptions},
    io::{stdin, stdout, Write},
};
use strategy::Shape;

mod agent;
mod evol;
mod game;
mod skill;
mod strategy;

const ARCHIVE: &str = "./archive";

type Variant = Normal;

fn main() {
    let shape = &Shape::new();
    let mut world = if let Ok(mut file) = File::open(ARCHIVE) {
        println!("+ 读取存档");
        let agents = std::iter::from_fn(|| Agent::new_read(shape, &mut file).ok());
        World::from_agents(agents, shape)
    } else {
        println!("+ 没有存档，新建世界");
        World::new(300, shape)
    };

    loop {
        println!("+ 训练t/对战p/退出q/显示权重s/技能列表l/导出智能a/导出规则r？");
        print!("? ");
        stdout().flush().unwrap();

        let mut buf = String::new();
        stdin().read_line(&mut buf).unwrap();

        match buf.trim() {
            "t" => {
                println!("+ 开始训练");
                for i in 0..500 {
                    world.cycle();
                    if i % 20 == 0 {
                        println!("+ {i}");
                    }
                }
                let mut archive = OpenOptions::new()
                    .write(true)
                    .create(true)
                    .open(ARCHIVE)
                    .unwrap();
                for agent in world.agents() {
                    archive.write_all(agent.data()).unwrap();
                }
                archive.flush().unwrap();
                println!("+ 训练完成");
            }
            "p" => {
                let rng = &mut thread_rng();
                let mut game: Game<_, _, Variant> =
                    Game::new((world.best(), shape, rng), Human, true);
                while game.round().is_none() {}
            }
            "q" => break,
            "s" => {
                world.best().show(shape);
            }
            "l" => {
                Variant::list();
            }
            "a" => {
                println!("{}", world.best().encode(shape));
            }
            "r" => {
                println!("{}", Variant::encode());
            }
            _ => continue,
        };
    }
}
